package com.mate.cloud.controller;

import com.mate.cloud.cache.annotation.RepeatSubmit;
import com.mate.cloud.protocol.response.BaseResponse;
import com.mate.cloud.protocol.web.controller.AdminBaseController;
import com.mate.cloud.constant.UserExceptionCodeEnum;
import com.mate.cloud.dto.UserInfoDTO;
import com.mate.cloud.exception.UserBusinessException;
import com.mate.cloud.service.ProductService;
import com.mate.cloud.service.RedisService;
import com.mate.cloud.service.UserService;
import com.mate.cloud.util.KeyUtils;
import com.mate.cloud.vo.RegisterVO;
import com.mate.cloud.vo.UserInfoVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@Tag(description = "user", name = "用户服务")
@RequestMapping("/user")
public class UserLikeController extends AdminBaseController {

    @Resource
    private UserService userService;

    @Resource
    private RedisService redisService;

    // redisson 实现分布式锁
    @Resource
    private Redisson redisson;

    @Resource
    private ProductService productService;


    @PostMapping("/register")
    @Operation(summary = "通过邮箱注册用户")
    public BaseResponse registerByEmail(@RequestBody @Valid RegisterVO registerVO, BindingResult bindingResult) {
        //校验不通过
        if (bindingResult.hasErrors()) {
            log.error("【用户注册】参数错误，form=${}", registerVO.toString());
            throw new UserBusinessException(UserExceptionCodeEnum.PARAMS_ERROR.getCode());
        }
        UserInfoDTO userInfoDTO = new UserInfoDTO();
        //赋值唯一的id
        userInfoDTO.setId(KeyUtils.genUniqueKey());
        BeanUtils.copyProperties(registerVO, userInfoDTO);
        userService.registerByEmail(userInfoDTO);
        return successCodeResponse();
    }

    @Operation(summary = "通过邮箱登录")
    @PostMapping("/login")
    public BaseResponse loginByEmail(@RequestParam("email") String email, @RequestParam("password") String password) {
        if (StringUtils.isEmpty(email) || StringUtils.isEmpty(password)) {
            throw new UserBusinessException(UserExceptionCodeEnum.EMAIL_PASSWORD_EMPTY.getCode());
        }
        UserInfoDTO dto = userService.loginByEmail(email, password);
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(dto, userInfoVO);

        return successBodyResponse(userInfoVO);
    }

    @RequestMapping("/like")
    @Operation(summary = "点赞")
    public BaseResponse like(@RequestParam("likedUserId") String likedUserId, @RequestParam("likedPostId") String likedPostId) {
        redisService.saveLiked2Redis(likedUserId, likedPostId);
        redisService.incrementLikedCount(likedUserId);
        return successCodeResponse();
    }

    /**
     * 防重复提交
     *
     * @param likedUserId
     * @param likedPostId
     * @return
     */
    @RequestMapping("/repeat")
    @RepeatSubmit(expireTime = 10)
    @Operation(summary = "repeat")
    public BaseResponse RepeatSubmit() {
        return successCodeResponse();
    }

    @PostMapping("/unlike")
    @Operation(summary = "取消点赞")
    public BaseResponse unlike(@RequestParam("likedUserId") String likedUserId, @RequestParam("likedPostId") String likedPostId) {
        redisService.unlikeFromRedis(likedUserId, likedPostId);
        redisService.decrementLikedCount(likedUserId);
        return successCodeResponse();
    }


    @RequestMapping("/red_lock")
    @Operation(summary = "分布式锁")
    public BaseResponse redLock(String userId) {

        return successBodyResponse(userId);
    }

    /**
     * 根据商品ID进行抢购
     *
     * @param productId
     * @return 商品抢购详情
     */
    @GetMapping("/order/{productId}")
    public BaseResponse sellGoods(@PathVariable String productId) {
        String result = productService.orderGoods(productId);
        return successBodyResponse(result);
    }

    /**
     * 根据商品ID进行查询余量
     *
     * @param productId
     * @return 商品抢购详情
     */
    @GetMapping("/query/{productId}")
    public BaseResponse queryGoods(@PathVariable String productId) {
        String result = productService.queryGoods(productId);
        return successBodyResponse(result);
    }

//    @RequestMapping("/redisson_lock")
//    public BaseResponse redissonLock() {
//        String key = "redisson_key";
//        for (int i = 0; i < 100; i++) {
//            new Thread(() -> {
//                log.info("=============线程开启============{}" , Thread.currentThread().getName());
//                boolean isGetLock = distributedLocker.tryLock(key, TimeUnit.SECONDS, 5L, 10L);
//                if (isGetLock) {
//                    try {
//                        Thread.sleep(100); //获得锁之后可以进行相应的处理
//                        log.info("======获得锁后进行相应的操作======" + Thread.currentThread().getName());
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                }
//            }, "lock").start();
//        }
//        return successCodeResponse();
//    }

    /**
     * 分布式锁Demo
     *
     * @return
     */
    @RequestMapping("readLock")
    public BaseResponse readLock() {
        String lockKey = "product_001";
        // 这里需要自己实例化不同redi实例的redisson客户端连接，这里只是伪代码用一个redisson客户端简化
        RLock lock1 = redisson.getLock(lockKey);
        RLock lock2 = redisson.getLock(lockKey);
        RLock lock3 = redisson.getLock(lockKey);

        /**
         * 根据多个RLock对象构建RedissonRedLock(最核心的差别就在这里)
         * Redis 底层大量使用了lua脚本
         *
         */
        RedissonRedLock redissonLock = new RedissonRedLock(lock1, lock2, lock3);
        try {
            /**
             * waitTimeout尝试获取锁的最大等待时间，超过这个值，则认为获取锁失败
             * leaseTime 锁的持有时间，超过这个时间锁会自动失效（值应设置为大于业务处理的时间，确保在锁有效期内业务能处理完成）
             */
            boolean res = redissonLock.tryLock(10, 30, TimeUnit.SECONDS);
            if (res) {
                // 成功获得锁，在这里处理业务
            }
        } catch (Exception e) {
            throw new RuntimeException("lock fail");
        } finally {
            // 无论如何，最后都要释放锁
            redissonLock.unlock();
        }
        return null;
    }
}
